from decorators import Transactional
from enums.menu_app_code_enum import MenuAppCodeEnum
from mauth.login_user_holder import LoginUserHolder
from models.menu import Menu
from models.role_menu import RoleMenu
from services import BaseService
from tools import build_tree, generate_id
from tools.db_tool import DbTool
from sqlalchemy import asc
from services.rbac_service import RbacService

rbac_service = RbacService()


class MenuService(BaseService):
    """
    菜单模块业务处理类
    """

    def app_list(self):
        """
        应用列表
        """
        res = []
        for member in MenuAppCodeEnum:
            res.append({"label": member.msg, "value": member.name.lower()})
        return res

    def list(self):
        """
        菜单列表
        """
        list = DbTool.filter_by_custom(Menu).order_by(asc(Menu.sort)).all()
        return Menu.to_dict_list(list)

    def tree(self):
        """
        菜单树
        """
        list = DbTool.filter_by_custom(Menu).order_by(asc(Menu.sort)).all()
        return build_tree(Menu.to_dict_list(list))

    @Transactional()
    def sync_route(self, routeList):
        """
        同步路由
        """
        menuIds = []
        if routeList is not None and len(routeList) > 0:
            # 一级菜单
            for route in routeList:
                isSync = route.get("isSync")
                if isSync == 1:
                    menuId = self.save_or_update_route(route, 0)
                    menuIds.append(menuId)
                    # 二级菜单
                    if (
                        route.get("children") is not None
                        and len(route.get("children")) > 0
                    ):
                        for child in route.get("children"):
                            menuId2 = self.save_or_update_route(child, menuId)
                            menuIds.append(menuId2)
                            # 三级菜单
                            if (
                                child.get("children") is not None
                                and len(child.get("children")) > 0
                            ):
                                for grandson in child.get("children"):
                                    menuId3 = self.save_or_update_route(
                                        grandson, menuId2
                                    )
                                    menuIds.append(menuId3)
                                    # 四级菜单
                                    if (
                                        grandson.get("children") is not None
                                        and len(grandson.get("children")) > 0
                                    ):
                                        for greatGrandson in grandson.get("children"):
                                            menuId4 = self.save_or_update_route(
                                                greatGrandson, menuId3
                                            )
                                            menuIds.append(menuId4)
        # 删除前端同步菜单、当前应用、不在当前同步菜单内的路由菜单
        appCode = LoginUserHolder.get_app_code()
        if len(menuIds) == 0:
            menuIds.append(0)
        self.db.session.query(Menu).filter(
            Menu.appCode == appCode, Menu.isSync == 1, Menu.id.notin_(menuIds)
        ).delete()

        sysMenus = (
            self.db.session.query(Menu)
            .filter(Menu.appCode == appCode, Menu.isSync == 1, Menu.id.notin_(menuIds))
            .all()
        )
        if len(sysMenus) > 0:
            deleteMenuIds = [menu.id for menu in sysMenus]
            # 删除冗余菜单
            self.db.session.query(Menu).filter(Menu.id.in_(deleteMenuIds)).delete()
            # 清除角色菜单关联表
            self.db.session.query(RoleMenu).filter(
                RoleMenu.menuId.in_(deleteMenuIds)
            ).delete()

    def save_or_update_route(self, route, pid):
        """
        保存或更新路由
        """
        menu = Menu(**route)
        menu.code = route.get("code")
        menu.appCode = LoginUserHolder.get_app_code()
        menu.parentId = pid
        menu.pids = ""
        if menu.openType is None:
            menu.openType = 0
        newPids = self.create_new_pids(pid)
        menu.pids = newPids
        menu.variable = "{}"
        oldMenu = self.get_menu(menu.code, pid)
        if oldMenu is not None:
            # 存在则更新
            menu.updateUser = LoginUserHolder.get_user_id()
            self.db.session.query(Menu).filter_by(id=oldMenu.id).update(menu.to_dict())
            return oldMenu.id
        else:
            menu.createUser = LoginUserHolder.get_user_id()
            menu.updateUser = LoginUserHolder.get_user_id()
            menu.id = generate_id()
            self.db.session.add(menu)
            return menu.id

    def create_new_pids(self, pid):
        if pid == 0:
            return "[" + str(pid) + "]" + ","
        else:
            # 获取父菜单
            parentMenu = self.db.session.query(Menu).filter_by(id=pid).first()
            if parentMenu is not None:
                print("pids:" + parentMenu.pids)
                return parentMenu.pids + "[" + str(pid) + "]" + ","

    def get_menu(self, code, pid):
        """
        根据code获取菜单
        """
        appCode = LoginUserHolder.get_app_code()
        return (
            self.db.session.query(Menu)
            .filter_by(code=code, parentId=pid, appCode=appCode)
            .first()
        )

    def user_route_menu(self):
        menu_list = (
            Menu.query.filter_by(enabled=1)
            .filter(Menu.type.in_([1, 2]))
            .order_by(asc(Menu.sort))
            .all()
        )
        currentUser = LoginUserHolder.get()
        user_id = currentUser.get("id")
        if currentUser.get("superAdmin", False) is not True:

            user_menu_ids = rbac_service.get_menu_ids_by_user_id(user_id)
            menu_list = [menu for menu in menu_list if menu.id in user_menu_ids]

        def map_menus_to_route_menus(menu_list):
            route_menu_list = []
            for menu in menu_list:
                meta = {
                    **({"title": menu.name} if menu.name else {}),
                    **({"icon": menu.icon} if menu.icon else {}),
                    **({"link": menu.url} if menu.openType == 3 else {}),
                    **({"iframeSrc": menu.url} if menu.openType == 4 else {}),
                    **({"hideInMenu": True} if menu.isShow != 1 else {}),
                    **({"keepAlive": True} if menu.isCache == 1 else {}),
                }

                if menu.isSync != 1:
                    sub_menu_buttons = get_sub_menu_buttons(menu)
                    meta.update(sub_menu_buttons)

                route_menu = {
                    "id": menu.id,
                    "parentId": menu.parentId or "0",
                    "name": menu.code,
                    "component": menu.component,
                    "path": menu.path,
                    "meta": meta,
                }
                route_menu_list.append(route_menu)
            return route_menu_list

        def get_sub_menu_buttons(menu):
            sub_menus = (
                Menu.query.filter_by(parentId=menu.id)
                .filter(Menu.type.in_([3, 4]))
                .order_by(asc(Menu.sort))
                .all()
            )
            return {sub_menu.code: sub_menu.name for sub_menu in sub_menus}

        return build_tree(map_menus_to_route_menus(menu_list))
